package com.jml.config;


import com.jml.proxy.FeignClientProxy;
import com.jml.proxy.JmlInvocationHandler;
import com.jml.utils.PackageUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.List;

/**
 * ImportBeanDefinitionRegistrar类自定义类放入spring容器中
 * 这里就是把所有实现feign注解的类统一放入容器中
 */
public class FeignClientsRegistrar extends DefaultListableBeanFactory implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, EnvironmentAware {

    @Autowired
    JmlInvocationHandler jmlInvocationHandler;

    private ResourceLoader resourceLoader;

    private Environment environment;

    public FeignClientsRegistrar() {
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
                                        BeanDefinitionRegistry registry) {
        init(registry);
        //registerFeignClient(registry);
    }

    /**
     * 测试：把这个类Jml放入容器中,BD的名字是com.jml.config.Jml
     *
     * @param registry
     */
    private void registerFeignClient(BeanDefinitionRegistry registry) {
        BeanDefinition definition = new RootBeanDefinition(Jml.class);
        BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, "com.jml.config.Jml", null);
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }


    private void init(BeanDefinitionRegistry registry) {
        // 1.使用java反射机制获取com.jml.member.api 下所有的接口，这些接口就是使用feign客户端的接口
        // 返回接口完整地址
        List<String> classNames = PackageUtil.getClassName("com.jml.member.api.service");
        classNames.forEach((className) -> {
            // 2.将代理对象注入IOC容器中
            Object classNameBean = null;
            Class<?> aClass = null;
            String simpleName = null;
            try {
                aClass = Class.forName(className);
                simpleName = aClass.getSimpleName();
                if (!StringUtils.isEmpty(simpleName)) {
                    simpleName += "Feign";
                }
            } catch (Exception e) {

            }
            if (aClass == null) {
                return;
            }
            if (classNameBean == null) {
                // 3.为每个接口生成代理对象
                Object proxy = FeignClientProxy.getFeignClientProxy().create(aClass);
                // 4.将代理类对象注册到IOC容器中
                /**
                 * 这一块这样写，而不是按照上面测试方法那样写的原因是：
                 * jdk生成的动态代理类的构造方法是有参构造，上面测试的那个是根据无参构造方法生成的，所以会报错
                 */
                //这是设定构造方法
                ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
                //这是设定构造方法的参数，第0个参数是jmlInvocationHandler
                constructorArgumentValues.addIndexedArgumentValue(0, jmlInvocationHandler);
                //之后会通过有参构造方法，构造proxy
                BeanDefinition definition = new RootBeanDefinition(proxy.getClass(), constructorArgumentValues, null);
                BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, className,
                        null);
                BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
            }
        });
    }
}
